<?php
header('Content-type:text/html; Charset=utf-8');
$mchid = '1556173941';          //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
$appid = 'wx41e7fa2bd4e8f219';  //微信支付申请对应的公众号的APPID
$appKey = 'd740b7ac62fd97cedae13c46ed1f73ad';   //微信支付申请对应的公众号的APP Key 开发者密钥?
$apiKey = '2YFIOP2nAvAa86uzWOcnw20GRf1efesV';
// file_put_contents('wxLogin.txt', serialize($apiKey)); //写入缓存
//https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
// 操作码 19960309zm+
// 证书号 6495E6C955A22675247FD6FBAE2AF56F257785D2
//①、获取用户openid

$wxPay = new WxpayService($mchid, $appid, $appKey, $apiKey);
$openId = $wxPay->GetOpenid();      //获取openid
if (!$openId) exit('获取openid失败');

//②、统一下单
if (!isset($_GET['ID']) || !$_GET['ID']) die('页面调用错误');
$retURL = 'location:';
if (!$_GET['retURL']) $_GET['retURL'] = '/';
if (substr($_GET['retURL'], 0, 1) == '/') {
  $retURL .= $_GET['retURL'];
} else {
  $retURL = $_GET['retURL'];
}
$ids = json_decode($_GET['ID'], true);
if (!is_array($ids)) {
  $ids = [$ids];
}

$outTradeNo = uniqid('wxpay');     //你自己的商品订单号
$payAmount = 0;          //付款金额，单位:元
$point = 0;              // 支付点券,积分
$orderName = '订单_' . json_encode($ids);    //订单标题
include_once('../code/core.php');
$res = GetData(['table' => 'orders', 'ID' => $ids], 'ID,sum,itemID,tradeNo');
// exit(json_encode($res,JSON_UNESCAPED_UNICODE));
// echo json_encode($res);
if (!$res) die('参数错误');
foreach ($res as $value) {
  $item = GetData(['table' => 'items', 'ID' => $value['itemID']], '*', true);
  $money = json_decode($value['money'], true);
  $money['sum']['CNY'] = $item['CNY'] * $value['sum'];
  $money['sum']['point'] = $item['point'] * $value['sum'];
  $tmp = UpData([
    'table' => 'orders',
    'tradeNo' => $outTradeNo,
    'money' => json_encode($money),
    'sellID' => $item['sellID'],
    'name' => $item['name'],
    'itemInfo' => json_encode($item),
    'where' => ['ID' => $value['ID'], 'state' => '1']
  ]);
  if (!$tmp) die('服务器繁忙!请稍后刷新');
  $payAmount += $CNY;
  $point += $p;
}
$payAmount /= 100;
if (!$payAmount) {
  header("location:/api/wxpay/orderquery.php?ID=" . $_GET['ID'] . '&retURL=' . $retURL);
  die;
}
// if ($res[0]['money'] == $res[0]['payment']) {
//   // header("location:");
//   header("location:/api/wxpay/orderquery.php?ID=" . $_GET['ID']);
//   die;
// }


$notifyUrl = 'http://api.x7654.cn/api/wxpay/orderquery.php';     //付款成功后的回调地址(不要有问号)
$payTime = time();      //付款时间
$jsApiParameters = $wxPay->createJsBizPackage($openId, $payAmount, $outTradeNo, $orderName, $notifyUrl, $payTime);
$jsApiParameters = json_encode($jsApiParameters);
?>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title><?php echo $orderName; ?></title>
  <script type="text/javascript">
    //调用微信JS api 支付
    function jsApiCall() {
      WeixinJSBridge.invoke(
        'getBrandWCPayRequest',
        <?php echo $jsApiParameters; ?>,
        function(res) {
          WeixinJSBridge.log(res);
          if (res.err_msg == 'get_brand_wcpay_request:ok') {
            alert('支付成功22！');
            window.location.href = "http://api.x7654.cn/api/wxpay/orderquery.php?ID=<?php echo $_GET['ID']; ?>";
          } else {
            alert('支付失败222：' + res.err_code + ', ' + res.err_desc + ', ' + res.err_msg);
          }
        }
      );
    }

    function callpay() {
      if (typeof WeixinJSBridge == "undefined") {
        if (document.addEventListener) {
          document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
        } else if (document.attachEvent) {
          document.attachEvent('WeixinJSBridgeReady', jsApiCall);
          document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
        }
      } else {
        jsApiCall();
      }
    }
  </script>
</head>

<body>
  <br />
  <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px"><?php echo $payAmount ?>元</span>钱</b></font><br /><br />
  <div align="center">
    <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;" type="button" onclick="callpay()">立即支付</button>
  </div>
</body>

</html>
<?php
class WxpayService
{
  protected $mchid;
  protected $appid;
  protected $appKey;
  protected $apiKey;
  public $data = null;
  public function __construct($mchid, $appid, $appKey, $key)
  {
    $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
    $this->appid = $appid; //微信支付申请对应的公众号的APPID
    $this->appKey = $appKey; //微信支付申请对应的公众号的APP Key
    $this->apiKey = $key;   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
  }
  /**
   * 通过跳转获取用户的openid，跳转流程如下：
   * 1、设置自己需要调回的url及其其他参数，跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
   * 2、微信服务处理完成之后会跳转回用户redirect_uri地址，此时会带上一些参数，如：code
   * @return 用户的openid
   */
  public function GetOpenid()
  {
    //通过code获得openid
    if (!isset($_GET['code'])) {
      //触发微信返回code码
      $scheme = $_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://';
      $uri = $_SERVER['PHP_SELF'] . $_SERVER['QUERY_STRING'];
      if ($_SERVER['REQUEST_URI']) $uri = $_SERVER['REQUEST_URI'];
      $baseUrl = urlencode($scheme . $_SERVER['HTTP_HOST'] . $uri);
      $url = $this->__CreateOauthUrlForCode($baseUrl);
      Header("Location: $url");
      exit();
    } else {
      //获取code码，以获取openid
      $code = $_GET['code'];
      $openid = $this->getOpenidFromMp($code);
      return $openid;
    }
  }
  /**
   * 通过code从工作平台获取openid机器access_token
   * @param string $code 微信跳转回来带上的code
   * @return openid
   */
  public function GetOpenidFromMp($code)
  {
    $url = $this->__CreateOauthUrlForOpenid($code);
    $res = self::curlGet($url);
    //取出openid
    $data = json_decode($res, true);
    $this->data = $data;
    $openid = $data['openid'];
    return $openid;
  }
  /**
   * 构造获取open和access_toke的url地址
   * @param string $code，微信跳转带回的code
   * @return 请求的url
   */
  private function __CreateOauthUrlForOpenid($code)
  {
    $urlObj["appid"] = $this->appid;
    $urlObj["secret"] = $this->appKey;
    $urlObj["code"] = $code;
    $urlObj["grant_type"] = "authorization_code";
    $bizString = $this->ToUrlParams($urlObj);
    return "https://api.weixin.qq.com/sns/oauth2/access_token?" . $bizString;
  }
  /**
   * 构造获取code的url连接
   * @param string $redirectUrl 微信服务器回跳的url，需要url编码
   * @return 返回构造好的url
   */
  private function __CreateOauthUrlForCode($redirectUrl)
  {
    $urlObj["appid"] = $this->appid;
    $urlObj["redirect_uri"] = "$redirectUrl";
    $urlObj["response_type"] = "code";
    $urlObj["scope"] = "snsapi_base";
    $urlObj["state"] = "STATE" . "#wechat_redirect";
    $bizString = $this->ToUrlParams($urlObj);
    return "https://open.weixin.qq.com/connect/oauth2/authorize?" . $bizString;
  }
  /**
   * 拼接签名字符串
   * @param array $urlObj
   * @return 返回已经拼接好的字符串
   */
  private function ToUrlParams($urlObj)
  {
    $buff = "";
    foreach ($urlObj as $k => $v) {
      if ($k != "sign") $buff .= $k . "=" . $v . "&";
    }
    $buff = trim($buff, "&");
    return $buff;
  }
  /**
   * 统一下单
   * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid
   * @param float $totalFee 收款总费用 单位元
   * @param string $outTradeNo 唯一的订单号
   * @param string $orderName 订单名称
   * @param string $notifyUrl 支付结果通知url 不要有问号
   * @param string $timestamp 支付时间
   * @return string
   */
  public function createJsBizPackage($openid, $totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp)
  {
    $config = array(
      'mch_id' => $this->mchid,
      'appid' => $this->appid,
      'key' => $this->apiKey,
    );
    //$orderName = iconv('GBK','UTF-8',$orderName);
    $unified = array(
      'appid' => $config['appid'],
      'attach' => 'pay',             //商家数据包，原样返回，如果填写中文，请注意转换为utf-8
      'body' => $orderName,
      'mch_id' => $config['mch_id'],
      'nonce_str' => self::createNonceStr(),
      'notify_url' => $notifyUrl,
      'openid' => $openid,            //rade_type=JSAPI，此参数必传
      'out_trade_no' => $outTradeNo,
      'spbill_create_ip' => '127.0.0.1',
      'total_fee' => intval($totalFee * 100),       //单位 转为分
      'trade_type' => 'JSAPI',
    );
    // die(json_encode($orderName,JSON_UNESCAPED_UNICODE));
    $unified['sign'] = self::getSign($unified, $config['key']);
    $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));
    //禁止引用外部xml实体
    libxml_disable_entity_loader(true);
    $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
    if ($unifiedOrder === false) {
      die('parse xml error');
    }
    if ($unifiedOrder->return_code != 'SUCCESS') {
      // die(json_encode($unified,JSON_UNESCAPED_UNICODE));
      die($unifiedOrder->return_msg);
    }
    if ($unifiedOrder->result_code != 'SUCCESS') {
      die($unifiedOrder->err_code);
    }
    $arr = array(
      "appId" => $config['appid'],
      "timeStamp" => "$timestamp",        //这里是字符串的时间戳，不是int，所以需加引号
      "nonceStr" => self::createNonceStr(),
      "package" => "prepay_id=" . $unifiedOrder->prepay_id,
      "signType" => 'MD5',
    );
    $arr['paySign'] = self::getSign($arr, $config['key']);
    return $arr;
  }
  public static function curlGet($url = '', $options = array())
  {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    if (!empty($options)) {
      curl_setopt_array($ch, $options);
    }
    //https请求 不验证证书和host
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
  }
  public static function curlPost($url = '', $postData = '', $options = array())
  {
    if (is_array($postData)) {
      $postData = http_build_query($postData);
    }
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
    if (!empty($options)) {
      curl_setopt_array($ch, $options);
    }
    //https请求 不验证证书和host
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
  }
  public static function createNonceStr($length = 16)
  {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $str = '';
    for ($i = 0; $i < $length; $i++) {
      $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $str;
  }
  public static function arrayToXml($arr)
  {
    $xml = "<xml>";
    foreach ($arr as $key => $val) {
      if (is_numeric($val)) {
        $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
      } else
        $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
    }
    $xml .= "</xml>";
    return $xml;
  }
  public static function getSign($params, $key)
  {
    ksort($params, SORT_STRING);
    $unSignParaString = self::formatQueryParaMap($params, false);
    $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
    return $signStr;
  }
  protected static function formatQueryParaMap($paraMap, $urlEncode = false)
  {
    $buff = "";
    ksort($paraMap);
    foreach ($paraMap as $k => $v) {
      if (null != $v && "null" != $v) {
        if ($urlEncode) {
          $v = urlencode($v);
        }
        $buff .= $k . "=" . $v . "&";
      }
    }
    $reqPar = '';
    if (strlen($buff) > 0) {
      $reqPar = substr($buff, 0, strlen($buff) - 1);
    }
    return $reqPar;
  }
}
